#include "VNS.h"

/* >>>>>>>>>>>>>>>>>> To be read from parameter file VNS.par <<<<<<<<<<<<<<<<<<<< */
/* Basic parameters */
double TimeUnit,	/* sec per unit time */
LocUpd,    			/* location update period */
SimLength, 			/* simulation length */
*Lanes,				/* */ 
LaneWidth, 			/* */
LaneLength,			/* */	
Pt,			        /* global transmission power level */
CSThresh,           /* Carrier Sense Threshold */
PowerMonitorThresh, /* Power Monitor Threshold i.e. receiver's sensitivity? */
noiseFloor,
systemLoss,
Frequency,          /* Operating frequency i.e. 5.9GHz for WAVE */
Gt,                 /* transmisster antenna's gain */
Gr,                 /* receiver antenna's gain */
antHeight,          /* antenna height both for transmitter and receiver */
SINR_DataCapture;   /* SINR for data capturing */

int ASlotTime,    	/* in TimeUnits */
HeaderTime,   		/* in TimeUnits */
SIFS,
AIFS,         		/* in slots */
CW,           		/* in slots */
Seed,         		/* seed for random gen */
Bitrate,      		/* in Mbps */
Verbose,	  		/* print log files? */
NLanes,				/* Speed of lane */
NumV,				/* Total number of vehicles in the simulation */
DataCaptureSwitch,
enabledLocUpd,
enabledLoop;

/* Parameters for broadcasting schemes and additional features */
int elScheme,		/* elastic scheme is enabled? */
elRate,				/* elastic rate */
jiScheme,	  		/* jitter scheme is enabled? */
jiSize,				/* jitter size */
lbScheme,	  		/* Location Based Beacon Scheduling (LBBS) scheme is enabled?*/
raScheme;			/* range assignment scheme is enabled? */
double raMBL, 		/* maximum beacon load: 3mbps */
raMaxRange, 		/* maximum range i.e. maximum transmission power */
raMinRange, 		/* minimum range i.e. minimum transmission power */
raE; 				/* step to increase communication range */
/* ________________________________________________________________________________ */

/* >>>>>>>>>>>>>> To be read from vehicle description file VNS.veh <<<<<<<<<<<<<<<< */
Vehicle 	*Vehicles;
activeVehicles aVehicles;
/* ________________________________________________________________________________ */

/* >>>>>>>>>>>>>>>>>>>>>>>>>>>> Global variables <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
double 		Time;							/* variable to keep the current time of simulation */
const char 	ParFile[] = "config/VNS.par";	/* configuration file with the main parameters */
const char 	VehFile[] = "config/VNS.veh";	/* input file contains the all vehicle description */
const char 	EvtFile[] = "log/VNS.evt"; 		/* file to record all events */
const char 	MsgFile[] = "log/VNS.msg"; 		/* file to record messages */
const char 	DmpFile[] = "log/dump"; 		/* dummy file for testing and checking */
char 		CplFile[50];
char 	   	DelFile[50]; 
FILE 		*MsgLog, *EvtLog, *DelayLog, *DumpLog, *CplLog;

double period;
double lambda; /* wavelength i.e. lambda  = Speed_Of_Light/Frequency */
double minRxPr;
int pndLine = 0;
/* ________________________________________________________________________________ */


/* 
 * void Error (const char * m)
 * Definition:
 * (a) This function prints error messages and terminate the simulator.
 * (b) The input parameter 'm' is the address of error message string.
 */
 
void Error (const char * m)
{
	printf ("%s\n", m); exit (-1);
}

/* 
 * void sqr (int x)
 * Definition:
 * (a) This function returns square of integer x.
 */
 
double sqr (int x)
{
	return (x*x);
}

/* 
 * void ReadPars ()
 * Definition:
 * (a) This function reads the main parameters from the paramater file VNS.par.
 * (b) The parameters are listed above.
 */
 
void ReadPars ()
{
	int i;
	FILE *f;

	if ((f = fopen (ParFile, "r")) == NULL) Error (ParFile);
	fscanf (f, "%le%*[^\n]\n%le%*[^\n]\n%le%*[^\n]\n%d%*[^\n]\n%d%*[^\n]\n%d%*[^\n]\n%d%*[^\n]\n%d%*[^\n]\n%d%*[^\n]\n", 
		    &SimLength, &LocUpd, &TimeUnit, &ASlotTime, &HeaderTime, &AIFS, &CW, &Seed, &Bitrate);
	
    fscanf (f, "%d%*[^\n]\n", &SIFS);
    	
    fscanf (f, "%d%*[^\n]\n%d%*[^\n]\n%d%*[^\n]\n%d%*[^\n]\n%d%*[^\n]\n%le%*[^\n]\n%le%*[^\n]\n%le%*[^\n]\n%le%*[^\n]\n%d%*[^\n]\n%d%*[^\n]\n%d%*[^\n]\n", 
	            &elScheme, &elRate, &jiScheme, &jiSize, &raScheme, &raMBL, &raMaxRange, &raMinRange, &raE, &lbScheme, &enabledLocUpd, &enabledLoop);
	
    fscanf (f, "%le%*[^\n]\n", &Pt);
    fscanf (f, "%le%*[^\n]\n", &CSThresh);
    fscanf (f, "%le%*[^\n]\n", &PowerMonitorThresh);
    fscanf (f, "%le%*[^\n]\n", &noiseFloor);
    fscanf (f, "%le%*[^\n]\n", &systemLoss);
    fscanf (f, "%le%*[^\n]\n", &Frequency);
    lambda = SPEED_OF_LIGHT/Frequency; /* lambda  = speed of light/Frequency i.e. wavelength */
    minRxPr = noiseFloor*modulation_table[1].SINR_ratio;
   
    fscanf (f, "%le%*[^\n]\n%le%*[^\n]\n%le%*[^\n]\n", &Gt,&Gr,&antHeight);
    fscanf (f, "%d%*[^\n]\n%le%*[^\n]\n", &DataCaptureSwitch,&SINR_DataCapture);

    fscanf (f, "%d%*[^\n]\n", &NumV);
	fscanf (f, "%d %le %le%*[^\n]\n", &NLanes, &LaneWidth, &LaneLength);
	if ((Lanes = (double *)malloc (sizeof(double)*NLanes)) == NULL) Error ("Malloc...");
	for (i=0; i<NLanes; i++) fscanf (f, "%le%*[^\n]\n", &Lanes[i]);
	fclose (f);
}

/* 
 * void initVehicleArrayValues(int s)
 * Definition:
 * (a) This function initializes the array variables of Vehicle structure.
 * (b) The input parameter 's' is the index of vehicle. Note, all vehicles are in a global variable 'Vehicles' array.
 */
 
void initVehicleArrayValues(int j)
{
	Vehicle *v = Vehicles;
	int i, d, k;

	for (i=0; i<NumV; i++) {
		v[j].Msg.Recvd[i] = FALSE;	
        v[j].Pr[i] = 0; /* no transmission */
        if(enabledLocUpd) {            
		    v[j].eCount[i] = 0; /* encounter number */
		    for(k=0; k<eIMaxNumber; k++) {
			    v[j].Link[i].eI[k].enteringTS = UNDEFINED;
			    v[j].Link[i].eI[k].leavingTS = UNDEFINED;
			    v[j].Link[i].eI[k].LT = SimLength;
			    v[j].Link[i].eI[k].FD = UNDEFINED;
			    v[j].Link[i].eI[k].NoM = 0;
			    v[j].Link[i].eI[k].theLastMsgRcvdTS  = UNDEFINED;
			    for(d=0; d<historyLength; d++) v[j].Link[i].eI[k].msgRcvdTSs[d] = UNDEFINED;
		    }
        }
	}
}

/* 
 * void ReadVehicles()
 * Definition:
 * (a) This function initializes all vehicles.
 * (b) All vehicles are stored in a global variable 'Vehicles' array.
 * (c) Some parameters (i.e. vehicle's initial location, speed) are initialized from vehicle description file VNS.veh whereas some are given as hard coded.
 */
 
void ReadVehicles ()
{
	int i;
	FILE *f;
	
	if ((f = fopen (VehFile, "r")) == NULL) Error (VehFile);
	if ((Vehicles = (Vehicle *)malloc (NumV*sizeof(Vehicle))) == NULL) Error ("Malloc...");

	for (i=0; i<NumV; i++) {
		Vehicle *v = &Vehicles[i];
		v->ExState = E_IDLE; v->Iter = 0; v->Content = FALSE;
		v->Xmit = v->Recs = v->Miss = 0; v->AIFS=0;
		if ((v->Msg.Reach = (int *)malloc (NumV*sizeof(int))) == NULL) Error ("Malloc...");
		if ((v->Msg.Recvd = (int *)malloc (NumV*sizeof(int))) == NULL) Error ("Malloc...");
		if(enabledLocUpd) if ((v->eCount = (int *)malloc (NumV*sizeof(int))) == NULL) Error ("Malloc...");
		if(enabledLocUpd) if ((v->Link = (Link *)malloc (NumV*sizeof(Link))) == NULL) Error ("Malloc...");		
        if ((v->Pr = (double *)malloc (NumV*sizeof(double))) == NULL) Error ("Malloc...");
    
		fscanf (f, "%le%*[^\n]\n%d%*[^\n]\n%le%*[^\n]\n%le%*[^\n]\n%le%*[^\n]\n%d%*[^\n]\n",
			&v->xloc, &v->yloc, &v->Range, &v->Period, &v->Phase, &v->DataSize);		
					
		v->aMsgDelay = (double) 8*v->DataSize / (Bitrate *1000000);
		v->Next = v->Phase;	
		v->initialPhase = v->Phase;
		v->Msg.Range = v->Range; /* for using range assignment algorithm, range is defined for each sending message */
		v->SMR = 0;
        v->Count = 0; /* for random backoff counting */
		v->doCount = 0; /* variable indicates whether a node should perform backoff counting */
 		v->NextMsgTime = v->Phase+v->Period; /* first time only */
        v->Pt = 0; /* No transmission power */        
        v->maxPr = noiseFloor; /* no transmission coming but only noise */
        v->cumlPr = noiseFloor;
        v->PhyState = PHY_SEARCHING;
		period = v->Period;
		
		initVehicleArrayValues(i);
	}
	aVehicles.aNumV = 0;
	if ((aVehicles.IDs = (int *)malloc (NumV*sizeof(int))) == NULL) Error ("Malloc...");		
	for(i=0;i<NumV;i++)	aVehicles.IDs[i] = UNDEFINED;

	fclose (f);
}

/* 
 * void PrintLine (int i)
 * Definition:
 */

void PrintLine (int i)
{
	Vehicle *v = &Vehicles[i];    
	if (Verbose)
		fprintf (EvtLog, "%12.7lf %5d %5d %5s %7.2lf %3d %3d %12.7lf\n", 
		Time, i, v->Iter, State2String[v->ExState], v->xloc, v->yloc, v->Count, v->Next);
}

/* 
 * void PrintHead (int i)
 * Definition:
 */
 
void PrintHead ()
{
	int i, d;

	printf ("VANET simulator, 06122010, JL, Tse\n\n");
	printf ("Parameters:\n");
	printf ("  Sim. lenght   	= %.7lf sec.\n", SimLength);
	printf ("  Loc. Update   	= %.7lf sec.\n", LocUpd);
	printf ("  TimeUnit      	= %e sec.\n", TimeUnit);
	printf ("  ASlotTime     	= %d time units\n", ASlotTime);
	printf ("  HeaderTime    	= %d time units\n", HeaderTime);
	printf ("  AIFS          	= %d slots\n", AIFS);
	printf ("  CW            	= %d slots\n", CW);
	printf ("  Seed          	= %d\n", Seed);
	printf ("  Bitrate       	= %d Mbps\n", Bitrate);
	printf ("  Elastic scheme   = %d\n", elScheme);
	printf ("  Elastic rate     = %d\n", elRate);
	printf ("  Jitter scheme   	= %d\n", jiScheme);
	printf ("  Jitter size     	= %d\n", jiSize);

	printf ("\n%d vehicles simulated\n", NumV);
	printf ("%5s %8s %8s %8s %8s %8s\n", "VEH", "Period", "Range", "Phase", "Datasize", "A Msg Delay");
	for (i=0; i<NumV; i++) {
		Vehicle *v = &Vehicles[i];
		printf ("%5d %8.5lf %8.2lf %8.5lf %8d %8.6lf\n", 
			i, v->Period, v->Range, v->Phase, v->DataSize, v->aMsgDelay);
    	//for (d=0; d<NumV; d++) if(v->eCount[d]<0)fprintf(DumpLog, "%d, %d - %d\n", i, d, v->eCount[d]);

	}

	printf ("\n%d lanes included of width %.2lfm and length %.2lfm\n", NLanes, 
		LaneWidth, LaneLength);
	for (d=0; d<NLanes; d++) printf ("Lane[%d]: %.2lf m/s\n", d,Lanes[d]);

	if (Verbose) {
		fprintf (EvtLog, "%12s %5s %5s %5s %7s %3s %3s %12s\n", "TIME", "VEH", "Iter", "ST", "X", "Y", "CNT", "NEXT"); 

		fprintf (MsgLog, "%7s %2s", "Time", "V");
		for (i=0; i<NumV; i++) {if(i>99)i-=100; fprintf (MsgLog, "%3d", i);}
		//fprintf (MsgLog, "\nReachable/receiVed");
		//for (i=0; i<NumV; i++) fprintf (MsgLog, "%5s", "R/V");
		fprintf (MsgLog, "%4s", "%\n");
	}
}

/* 
 * double RangeAssignment(int j)
 * Definition:
 * (a) This function implements a range assignment algorithm proposed by Marc Torrent Moreno etal., i.e. D-FPAV.
 * (b) The function need to be revised. 
 */
 
double RangeAssignment(int j)
{
	Vehicle * v = Vehicles;
	int i;
	double load = 0;

	for (i=0; i<NumV; i++) {
		Vehicle *w = &Vehicles[i];
			if (v[i].Msg.Recvd[j]) { 
				if(raMinRange > v[i].Msg.Range) raMinRange = v[i].Msg.Range;
				load = load + v[i].DataSize*8*(1/w->Period);
			}
	}
	if(load < raMBL && (v[j].Range + raE) < raMaxRange) {
		if(raMinRange<(v[j].Range + raE) && raMinRange > v[j].Range) v[j].Range = raMinRange; 
		else v[j].Range += raE;
		v[j].Msg.Range = v[j].Range; 
	}
}

/* 
 * void ProcessMessage (int i)
 * Definition:
 * (a) This function calculates the metrics and creates neccessary log information. 
 */
 
void ProcessMessage (int i)
{
	int j, k, tRe, tRv;
	double SMR, nearestToSimEnd, tmpNoM = UNDEFINED;
	Vehicle *v = Vehicles;
	tRe = tRv = 0;  
	nearestToSimEnd = SimLength;
	
	if (Verbose) fprintf (MsgLog, "%7.5lf %2d", Time-(v[i].aMsgDelay+HeaderTime*TimeUnit), i); 

	for (j=0; j<NumV; j++) {		
		if( v[j].Next >= SimLength && nearestToSimEnd >= v[j].Next) nearestToSimEnd = v[j].Next;
	
		if (v[i].Msg.Reach[j] && i != j) {
			tRe++;
			if (v[i].Msg.Recvd[j]) { 
				tRv++;
				if (Verbose) fprintf (MsgLog, "  1");  
                if(enabledLocUpd) {				
                    k = v[j].eCount[i];
				    if(v[j].Link[i].eI[k-1].FD == UNDEFINED)
					    v[j].Link[i].eI[k-1].FD = v[j].Link[i].eI[k-1].msgRcvdTSs[0] - v[j].Link[i].eI[k-1].enteringTS;

				    /* tmpMD is for keeping the delay between two consecutive successfull receptions*/
				    if(v[j].Link[i].eI[k-1].leavingTS == UNDEFINED)				
				    {
					    //if(v[j].Link[i].eI[k-1].theLastMsgRcvdTS != UNDEFINED)
					    tmpNoM = v[j].Link[i].eI[k-1].msgRcvdTSs[0] - v[j].Link[i].eI[k-1].theLastMsgRcvdTS;						
					    if(tmpNoM > v[j].Link[i].eI[k-1].NoM) v[j].Link[i].eI[k-1].NoM = tmpNoM;
					     				
					    v[j].Link[i].eI[k-1].theLastMsgRcvdTS = v[j].Link[i].eI[k-1].msgRcvdTSs[0];
				    }				
                }
			}
			else if (Verbose) fprintf (MsgLog, "  0"); 		
		} else 
		{   
			if (Verbose) fprintf (MsgLog, "  .");
		}
	}

	SMR = tRe>0?(100*tRv)/tRe:0.0;
	v[i].SMR = SMR;
	
	if (Verbose) fprintf (MsgLog, "%4d\n", (int)SMR);
	v[i].Xmit++; v[i].Recs += tRe; v[i].Miss += tRe-tRv;
	
	if(raScheme)RangeAssignment(i);
}


/*
 * void PrintSummary()
 * Definition:
 * a) This function prints the summary of the simulation in screen as well as in file.
 */
 
void PrintSummary ()
{
	Vehicle *v = Vehicles;
	int i, j, k;
	double avgSMR = 0, allRecs = 0, allMiss = 0;
	double avgNb = 0;
	double tmpNoM = UNDEFINED;
	int *NoM, *FD;
	int cdNoM = 0, cdFD = 0, d;
	int Links=0;
	double totalTXs=0;
    int fd100ms = 0;
    int fd100ms_200ms = 0;
	int fd200ms_1s = 0;
	int fd1s_5s = 0;
	int fd5s_60s = 0;
	int fdNever = 0;
    double cumulativeLoss = 0;
	
	
	printf ("\nTotals\n\n");
	printf ("%4s %10s %10s %10s %5s %5s\n", "VEH", "XMIT", "TOT.RECVRS", "TOT.MISSED", "SMR%", "#NB");

		
	for (i=0; i<NumV; i++) {
		
		v[i].SMR = v[i].Recs>0?(double)(100*(double)(v[i].Recs-v[i].Miss)/(double)v[i].Recs):0;

		printf ("%4d %10d %10d %10d %5d %5d\n", i, v[i].Xmit, v[i].Recs, v[i].Miss, (int)v[i].SMR, v[i].Xmit>0?v[i].Recs/v[i].Xmit:0);

		allRecs  += v[i].Recs;
		allMiss  += v[i].Miss;		
		avgSMR   += v[i].SMR;
		avgNb    += (v[i].Xmit>0?(double)v[i].Recs/(v[i].Xmit-1):0.0);
		totalTXs += v[i].Xmit;
	
		for (j=0;j<NumV;j++)
		{
			if(i != j) 
			{
                if(enabledLocUpd) {
				    k = v[j].eCount[i]; /* k == 0 means j never was in Nb of i */
				    if(k > 0) {
					    if(v[j].Link[i].eI[k-1].leavingTS == UNDEFINED)
					    {
						    v[j].Link[i].eI[k-1].LT = SimLength - v[j].Link[i].eI[k-1].enteringTS;
						    if(v[j].Link[i].eI[k-1].FD == UNDEFINED) v[j].Link[i].eI[k-1].FD = v[j].Link[i].eI[k-1].LT;				
						
						    tmpNoM = SimLength - v[j].Link[i].eI[k-1].theLastMsgRcvdTS;
						    if(tmpNoM > v[j].Link[i].eI[k-1].NoM) v[j].Link[i].eI[k-1].NoM = tmpNoM;
					    }		
				    }	
                }
			}
		}
	} 

	fprintf (DumpLog, "Avg SMR1: %3.3f, Avg Nb: %3.3f\n", avgSMR/NumV, avgNb/NumV);
	fprintf (DumpLog, "Avg SMR2: %3.3f, Avg Nb: %3.3f\n", 100*(allRecs-allMiss)/allRecs, avgNb/NumV);

	//printf ("Avg SMR1: %3.3f, Avg Nb: %3.3f\n", avgSMR/NumV, avgNb/NumV);
	printf ("Avg SMR2: %3.3f, Avg Nb: %3.3f, AllTX:%3.1f, All recv: %3.1f, pndLine:%d\n", 100*(allRecs-allMiss)/allRecs, avgNb/NumV, totalTXs, allRecs-allMiss, pndLine);
	printf ("Lambda: %5.5f, Pr: %2.2e\n", lambda, TwoRay(Pt, Gt, Gr, antHeight, antHeight, systemLoss, 70, lambda));


    printf ("CS range: %5.5f\n", getDist(CSThresh,Pt, Gt, Gr, antHeight, antHeight, systemLoss, lambda));
    printf ("CS range: %5.1f(FreeSpace)\n", getDistFriis(CSThresh,Pt, Gt, Gr, lambda));   
    printf ("RX range: %5.1f(FreeSpace)\n", getDistFriis(noiseFloor*modulation_table[1].SINR_ratio,Pt, Gt, Gr, lambda));   
    printf ("PwrSense range: %0.1f(FreeSpace)\n", getDistFriis(PowerMonitorThresh,Pt, Gt, Gr, lambda));   
    printf ("PwrSense range: %0.1f\n", getDist(PowerMonitorThresh,Pt, Gt, Gr, antHeight, antHeight, systemLoss, lambda));
	//printf ("Freq: %4.13f, RXThresh: %3.13f\n", Frequency, RXThresh);
	
    /* Calculate CDF of FD and NoM */	
    
    if(enabledLocUpd) {        
        if ((NoM = (int *)malloc ((SimLength+1)*sizeof(int))) == NULL) Error ("Malloc...");
	    if ((FD = (int *)malloc ((SimLength*10)*sizeof(int))) == NULL) Error ("Malloc...");

	    for(j=0;j<NumV;j++)	{
		    for (i=0; i<NumV; i++) {
			    k = v[j].eCount[i];		
			    if(v[j].eCount[i] > 0) {			
				    for(k = v[j].eCount[i]; k>0; k--)
				    {
					    //if(v[j].Link[i].eI[k-1].FD>0)
					    FD[(int)(10*v[j].Link[i].eI[k-1].FD)]++;
					    NoM[(int)(v[j].Link[i].eI[k-1].NoM)]++;
					    Links++;
					    if(v[j].Link[i].eI[k-1].enteringTS != UNDEFINED && v[j].Link[i].eI[k-1].leavingTS != UNDEFINED && v[j].Link[i].eI[k-1].LT == v[j].Link[i].eI[k-1].FD) 
					    {	if(v[j].Link[i].eI[k-1].LT >= 1.5)fdNever++;}
					    else {
						    if(v[j].Link[i].eI[k-1].FD <= 0.1) fd100ms++;
						    if(v[j].Link[i].eI[k-1].FD <= 0.2 && v[j].Link[i].eI[k-1].FD > 0.1) fd100ms_200ms++;					
						    if(v[j].Link[i].eI[k-1].FD <= 1 && v[j].Link[i].eI[k-1].FD > 0.2) fd200ms_1s++;					
						    if(v[j].Link[i].eI[k-1].FD <= 5 && v[j].Link[i].eI[k-1].FD > 1) fd1s_5s++;
						    if(v[j].Link[i].eI[k-1].FD > 5) fd5s_60s++;
					    }									
				    }
			    }
		    }
	    } 
	
	    fprintf (DumpLog, "#links: %d\n", Links);	
	    fprintf (DumpLog, "0.1s 	   %d\n0.1s-0.2s  	%d\n0.2s-1s     %d\n1s-5s 	%d\n5s-60s 	%d\nNever 	%d\n", fd100ms, fd100ms_200ms, fd200ms_1s, fd1s_5s, fd5s_60s, fdNever);	

	    for(i=0;i<SimLength+1;i++)	{
			    cdNoM += NoM[i];
			    fprintf (DelayLog, "%3d	%3.5f\n",i, (double)(cdNoM)/(double)(Links));
	    }

	    for(i=0;i<=SimLength*10;i++)	{
			    cdFD += FD[i];
			    //fprintf (DelayLog, "%3.5f	%3.5f\n",(double)(i)/(double)10, (double)cdFD/(double)Links);
	    }  
    }	
	
}

/*
 * void SINR (int j)
 * Definition:
 * a) This function returns the Signal to Interference-plus-Noise Ratio.
 */
double SINR(int s, int r) {
    Vehicle *v = Vehicles;	
    if ((v[r].cumlPr-v[r].Pr[s])<= (noiseFloor-1e-15)) Error("ERROR: cumlPr - Pr < noiseFloor."); //return 0.0; 
	return v[r].Pr[s]/(v[r].cumlPr-v[r].Pr[s]);
}

/*
 * void CanReach(int i, int j)
 * Definition:
 * a) This function checks if j is in the communication range of i i.e. if j can receive a signal from i.
 * b) The communication range is the furthest distance where a receiver can 
 *    receive message from the sender in the absence of any interference (except noise floor.)
 * c) The minRxPr is the minimum energy level at the receiver that the receiver can receive the message.
 *    The minRxPr = noiseFloor * SINR;
 */
 
int CanReach (int s, int r)
{
	Vehicle *v = Vehicles;
    double Pr;
	double sqry = sqr (LaneWidth*(v[s].yloc-v[r].yloc));
    double dist = sqrt(sqr(v[s].xloc-v[r].xloc)+sqry);
    
    if(enabledLoop) {	
        double ll = v[s].xloc-LaneLength;
	    double rl = v[s].xloc+LaneLength;
        double ldist = sqrt(sqr(ll-v[r].xloc) + sqry);
        double rdist = sqrt(sqr(rl-v[r].xloc) + sqry);
        if(dist > ldist) dist = ldist;
        if (dist > rdist) dist = rdist; 
    }

    Pr = TwoRay(Pt, Gt, Gr, antHeight, antHeight, systemLoss, dist, lambda); 
    if(Pr >= minRxPr) return 1; // This is by RX Range
    
    return 0;
}
/*
 * void CanSense(int i, int j)
 * Definition:
 * a) This function checks if j is in the interference range of i i.e. if j can sense the signal from i.
 */
 
int CanSense (int s, int r)
{
	Vehicle *v = Vehicles;
    double Pr;
	double sqry = sqr (LaneWidth*(v[s].yloc-v[r].yloc));
    double dist = sqrt(sqr(v[s].xloc-v[r].xloc)+sqry);
    if(enabledLoop) {
	    double ll = v[s].xloc-LaneLength;
	    double rl = v[s].xloc+LaneLength;
        double ldist = sqrt(sqr(ll-v[r].xloc) + sqry);
        double rdist = sqrt(sqr(rl-v[r].xloc) + sqry);
        if(dist > ldist) dist = ldist;
        if (dist > rdist) dist = rdist; 
    }

    Pr = TwoRay(Pt, Gt, Gr, antHeight, antHeight, systemLoss, dist, lambda); 

    if((Pr) >= PowerMonitorThresh)  return 1;

    else return 0;
}
/*
 * void UpdateAffectedNodes(int i)
 * Definition:
 * a) This function checks if j is in the interference range of i i.e. if j can sense signal from i.
 */
 
void UpdateAffectedNodes (int s)
{
	Vehicle *v = Vehicles;
	double ll, rl, sqry,sqrx, dist, ldist, rdist, Pr;
    int r;

    if(enabledLoop) {
        ll = v[s].xloc-LaneLength;
        rl = v[s].xloc+LaneLength;    
    }

    for (r=0; r<NumV; r++) {
        if(s != r) {      
            if(v[s].Pt == Pt) {
                sqry = sqr (LaneWidth*(v[s].yloc-v[r].yloc));
                dist = sqrt(sqr(v[s].xloc-v[r].xloc)+sqry);
                if(enabledLoop) {                
                    ldist = sqrt(sqr(ll-v[r].xloc) + sqry);
                    rdist = sqrt(sqr(rl-v[r].xloc) + sqry);
                    if(dist > ldist) dist = ldist;
                    if (dist > rdist) dist = rdist;
                }

                Pr = TwoRay(Pt, Gt, Gr, antHeight, antHeight, systemLoss, dist, lambda); 

                if(Pr >= PowerMonitorThresh){
                    v[r].Pr[s] = Pr;             
                    v[r].cumlPr += Pr;
                }
            } else if (v[s].Pt == 0 && v[r].Pr[s] > 0) {
                v[r].cumlPr -= v[r].Pr[s];    
                v[r].Pr[s] = 0;
                if(v[r].PhyState == s) v[r].PhyState = PHY_SEARCHING;
            }
        }
    }
}
/*
 * void GetNext()
 * Definition:
 * a) This function returns the vehicle with the nearest transmission.
 */

int GetNext ()
{
	int i = 0, d;

	for (d=0; d<NumV; d++) 
		if (Vehicles[i].Next>=Vehicles[d].Next) i=d;

	aVehicles.aNumV = 0;
	
	for (d=0; d<NumV; d++) 
		if (Vehicles[i].Next==Vehicles[d].Next) {aVehicles.IDs[aVehicles.aNumV] = d;aVehicles.aNumV++;}
			
	return (i);
}

/*
 * void CheckBusy(int i)
 * Definition:
 * a) This function checks the medium whether it is idle or busy in the CR of the vehicle "i".
 * b) If there is no transmission(s), then it return 0 otherwise it returns 1. (Better to return the time at which channel becomes free again.)
 */
 
double CheckBusy (int i)
{
	Vehicle *v = Vehicles;

    /* If node is in middle of receiving message or cumulative power is more the CSThresh then MAC should be informed busy */
    if(v[i].PhyState != PHY_SEARCHING || v[i].cumlPr >= CSThresh) return 1;

    return 0;
}


/*
 * void UpdateLinks()
 * Definition:
 * a) This function updates the Link between vehicles.
 * b) eCount counts the number of encounters.
 * c) eI keeps the information (e.g. entering, leaving times and its length) of each encounter intance.
 * d) theLastMsgRcvdTS of eI is used to calculate NoM and it should actually hold the timestamp of the last successfully received message from i to j since the eI is established. 
 */

void UpdateLinks ()
{
	Vehicle *v = Vehicles;
	int i, j, k = 0;

	for (i=0; i<NumV; i++) {
		for (j=0; j<NumV; j++) {
			if(i != j)
			{
				k = v[j].eCount[i];
				if(CanReach (i, j)) {
					if(k == 0 || (k > 0 && v[j].Link[i].eI[k-1].leavingTS != UNDEFINED)) {							
						v[j].eCount[i] = v[j].eCount[i] + 1;
						k = v[j].eCount[i];
						// recording kth entering time of v_j into the CR of v_i 
						v[j].Link[i].eI[k-1].enteringTS = Time; 
						v[j].Link[i].eI[k-1].theLastMsgRcvdTS = Time; // I give entering time to initialize theLastMsgRcvdTS
					}
				} else if (k > 0 && v[j].Link[i].eI[k-1].enteringTS != UNDEFINED && v[j].Link[i].eI[k-1].leavingTS == UNDEFINED) {	
					// recording kth leaving time of v_j from the CR of v_i
					v[j].Link[i].eI[k-1].leavingTS = Time;
					// Link life time i.e. LT. May be not neccessary since there are leaving and entering TSs.
					v[j].Link[i].eI[k-1].LT = v[j].Link[i].eI[k-1].leavingTS - v[j].Link[i].eI[k-1].enteringTS;
					if(v[j].Link[i].eI[k-1].FD == UNDEFINED) v[j].Link[i].eI[k-1].FD = v[j].Link[i].eI[k-1].LT;

				}
			}
		}
	}
}

/*
 * void UpdateLocations()
 * Definition:
 * a) This function updates the location of vehicles as function of time.
 * b) There is LocUpdate parameter that can be configured from VNS.par file. This parameter sets the update rate of location.
 */

void UpdateLocations ()
{
	Vehicle *v = Vehicles;
	int i;

	for (i=0; i<NumV; i++) {
        double x = v[i].xloc + Lanes[v[i].yloc]*LocUpd; /* LocUpd must be considered to update locations */
		if (x<0) x += LaneLength;
		if (x>LaneLength) x-= LaneLength;
		v[i].xloc = x;
	}
	UpdateLinks(); /* For recording encounter interval eI and entering and leaving times */
}

/*
 * void StartRxRecording(int s)
 * Definition:
 * a) This function records the receptions of a message and its collisions.
 * b) The input parameter "s" is the vehicle currently active i.e. sending.
 */

void StartRxRecording(int s)
{
	Vehicle *v = Vehicles;
	int r = 0, i = 0,  k = 0, d = 0;

	for (r=0; r<NumV; r++) {
		if(s != r)
		{
			if (CanReach (s, r))  /* if node is able to receive when there is no intereference at all, it means that the node can be reachable */
            {
                v[s].Msg.Reach[r] = TRUE;
                if(enabledLocUpd) {
                    k = v[r].eCount[s];

				    for(d=historyLength-1;d>0;d--) {
						    v[r].Link[s].eI[k-1].msgRcvdTSs[d] = v[r].Link[s].eI[k-1].msgRcvdTSs[d-1];
				    }
					
				    v[r].Link[s].eI[k-1].msgRcvdTSs[0] = v[s].Next;
                }
            }
            else {    
                v[s].Msg.Reach[r] = FALSE;
                if(enabledLocUpd)                
                    if(v[r].eCount[s]>0) {k = v[r].eCount[s]; v[r].Link[s].eI[k-1].msgRcvdTSs[0] = UNDEFINED;}
            }       		
            
            if(v[r].PhyState == PHY_SEARCHING && SINR(s, r) >= modulation_table[1].SINR_ratio) {
               v[s].Msg.Recvd[r] = TRUE; 
               v[r].PhyState = s;

               for (i=0; i<NumV; i++) {								
                  if(i != s && v[i].ExState == E_XMIT) {       							        
                     if(i != r && SINR(i, r) < modulation_table[1].SINR_ratio && CanReach(i, r)) {
                        v[i].Msg.Recvd[r] = FALSE; 
                        if(enabledLocUpd) {                        
                            k = v[r].eCount[i]; 
                            v[r].Link[i].eI[k-1].msgRcvdTSs[0] = UNDEFINED;
                        }
                     }
			      }
               }

             }
             else { 
                  v[s].Msg.Recvd[r] = FALSE;
                  /*below, we need to check if the currently receiving message is still free of collision. */  
                  if(v[r].PhyState != PHY_SEARCHING && SINR(v[r].PhyState, r) < modulation_table[1].SINR_ratio) {
                        v[v[r].PhyState].Msg.Recvd[r] = FALSE;
                        if(enabledLocUpd) {
                            k = v[r].eCount[v[r].PhyState];
                            v[r].Link[v[r].PhyState].eI[k-1].msgRcvdTSs[0] = UNDEFINED;
                        }
                  }
             }  			
		}
	}
}

/*
 * void BroadcastingSchemes(int i)
 * Definition:
 * a) This function implements broadcasting schemes that can work on IEEE 802.11p DCF function.
 * b) The input parameter "i" is the vehicle currently active.
 * c) Each of them can be enabled/disabled from parameter configuration file VNS.par.
 * Conventions:
 * a) Any 802.11p compliant broadcasting schemes can be added. 
 * b) Each scheme should have a brief description and name.
 * c) All relevant scheme variables should have name started with one or two letters abbreviation of the scheme name.
 * d) All schemes should have configuration parameters including enable/disable parameter from VNS.par configuration file.
 */

void BroadcastingSchemes(int i)
{
  	Vehicle *v = Vehicles;
	/* Elastic Periodic Broadcasting (EPB) Scheme */
	if(v[i].Iter%elRate==0 && elScheme) {
  		v[i].Phase = (double)((v[i].Period)*((double)random()/RAND_MAX));
		if(jiScheme)v[i].initialPhase = v[i].Phase;
    }

	/* Jitter Added Periodic Broadcasting(JPB) Scheme */				
    if(jiScheme && !lbScheme) {
  		v[i].Phase = v[i].initialPhase + (double)(((v[i].aMsgDelay*jiSize)*((double)random()/RAND_MAX)) - (v[i].aMsgDelay*jiSize/2)); 
    }

    /* (Fully Distributed-) Location Based Broadcast Scheduling (LBBS) Scheme */				
    if(lbScheme) {
        v[i].Range = getDistFriis(noiseFloor*modulation_table[1].SINR_ratio,Pt, Gt, Gr, lambda);
		v[i].Phase = ((double)((int)(v[i].xloc)%(int)(2*v[i].Range)))/(20*v[i].Range);
		if(jiScheme)
		v[i].Phase += (double)(((v[i].aMsgDelay*jiSize)*((double)random()/RAND_MAX)));// - (v->aMsgDelay*jiSize/2)); 
    }
}

/* 
 * void Move(int i) 
 * Definition:
 * a) This function implements the CSMA/CA of IEEE 802.11p DCF's core function in broadcast communication mode.
 * b) It uses CheckBusy function to read the channel condition.
 * c) It uses StartRxRecording functions to record the receptions and collisions of messages.
 * d) It uses ProcessMessage function to calculate the relevant metrics.
 * f) It uses BroadcastingSchemes function to attach additional broadcasting schemes that can work on top of 802.11p DCF.
 * Conventions:
 * a) No time adjustment upon state change; time is the responsibility
 *    of the target state
 * b) Assume state inspection is at the beginning of a time unit. 
 * c) Initialize relevant state variables at the source state 
 * d) Throughout the code, we use the following letters as
 * 		(1) "i" for the vehicle in target 
 *		(2) "j" for representing vehicles in CR of "i" 
 *		(3) "p" for the third vehicle causing collision
 * 		(4) "d" is dummy   
 */

void Move (int i)
{
  	Vehicle *v;
  	int printTail;
  	double MediumBusy;
  	
	int d = 0;
	for(d = 0; d < aVehicles.aNumV; d++)
	{
		i = aVehicles.IDs[d];
	  	v = &Vehicles[i];
	  	printTail = 0;
	  	MediumBusy = CheckBusy (i);
		//if(v->ExState == E_COUNTING && v->Count > 0);
	  	//else if(Time>8 && Time < 10 &&  v->AIFS == 0) { fprintf (DumpLog, "%8d[%3d]\t%15s", (int)(Time*1000000), i, State2String[v->ExState]); printTail = 1;}
	  	
	  	switch (v->ExState) {
	  
			case E_IDLE: 
			  	v->ExState = E_WAITAIFS;
			  	v->AIFS    = AIFS*ASlotTime + SIFS;
			  	v->Count   = 0;
			  	v->doCount = 0;
			  	break;			  	
			case E_WAITAIFS:
		  		if (MediumBusy == 1) v->ExState = E_DEFERRING;
		  		else if (v->AIFS == 0) {
		    			v->ExState = E_COUNTING;
				    	if ( v->Count == 0 && v->doCount==1 ) {
		    	  			v->Count = (int)((CW+1)*(((double)random())/(RAND_MAX))); /* CW >= Count must be >= 0 */
		    	  			v->slotCount = ASlotTime;
		    	  		} 
		    		} else {
					v->AIFS -= 1;
					v->Next += TimeUnit;
			  	}
			  	break;
				          
			case E_DEFERRING: 
                if (MediumBusy == 1)  v->Next += TimeUnit;
                else {  				
		       		v->ExState = E_WAITAIFS;
				    v->AIFS   = AIFS*ASlotTime + SIFS;
				    v->doCount = 1;
                }
                break;
			case E_COUNTING: 
				if(v->doCount == 1) v->doCount = -v->doCount;
				if (v->Count == 0){
					v->ExState = E_XMIT;
                    v->Pt = Pt; /* set transmission power */
                    UpdateAffectedNodes(i); /* change Pr of possibly affected nodes */
					v->Next += v->aMsgDelay+HeaderTime*TimeUnit;						
                    StartRxRecording(i); 
			  	} else if (MediumBusy == 1) {
		    		v->ExState = E_WAITAIFS;
		    		v->AIFS    = AIFS*ASlotTime + SIFS;
		    		v->slotCount = ASlotTime;
				} else {
					v->slotCount--;
		    		if(v->slotCount == 0){v->Count -= 1; v->slotCount = ASlotTime;}
		    		v->Next  += TimeUnit;
		  		}    	

		  		break;
	  
			case E_XMIT:
		  		v->ExState = E_IDLE;
		    	v->Pt = 0; /* set Pt to 0 power i.e. no transmission */
                UpdateAffectedNodes(i); /* change Pr of possibly affected nodes */
		  		v->Iter   += 1;
		  		v->Next    = v->NextMsgTime;
		  		/* Attach broadcasting schemes if enabled. */
				BroadcastingSchemes(i);
		  		/* Compute NextMsgTime */			
		  		v->NextMsgTime = v->Period*(v->Iter+1)+v->Phase;
		  		ProcessMessage (i);
		  		
		  		break;
		}

		if(printTail) fprintf (DumpLog, " ->%10s(%d)\n", State2String[v->ExState], v->Count);
	}

 	if (v->Next >= v->NextMsgTime && v->Iter > 1) {
    	/* Message dropped, handle and reinitialize */
		/*Variant A: new message starts everything from 0 i.e. starts listening AIFS etc. */
		/*Variant B: new message simply overwrite the current message and continue what it is doing. */		
		//if(i==5)
        fprintf (DumpLog, "Vehicle %3d's Msg is dropped (Next:%7.6f-%7.6f), iter %d, %d, xloc:%6.1f\n", i,v->Next, v->NextMsgTime, v->Iter, v->ExState, v->xloc);	
  	}
}


/* 
 * void ShowProgress (double displayRate)
 * Definition:
 * (a) This function displays the current progress of the simulation in percentage. 
 * (b) The input parameter displayRate sets how often the displaying should occur. 
 */
 
void ShowProgress (double displayRate)
{
	static double progress = 0;
	double currentProgress = 100*(Time/SimLength); 

	if(currentProgress - progress > displayRate) {
		progress = currentProgress; 
		printf("Progress - %3.2f%%", progress);
		printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
	}	
}

int main (int argc, char *argv[])
{
	int i;
	double NextLocUpd;

	Time = 0; NextLocUpd = LocUpd;
	Verbose = argc == 2;

	if ((DumpLog = fopen (DmpFile, "w")) == NULL) Error(DmpFile);
	
	ReadPars ();
	ReadVehicles ();
	
	if(elScheme) {sprintf(DelFile,"log/e%dDelay%d", elRate, NumV); sprintf(CplFile,"log/e%dCPL%d", elRate, NumV);}
	if(jiScheme) {sprintf(DelFile,"log/j%dDelay%d",jiSize, NumV);  sprintf(CplFile,"log/j%dCPL%d",jiSize, NumV);}
	if(lbScheme && !jiScheme){  sprintf(DelFile,"log/lbDelay%d", NumV);sprintf(CplFile,"log/lbCPL%d",NumV);}
	if(lbScheme && jiScheme)  sprintf(DelFile,"log/j%dlbDelay%d", jiSize, NumV);
	if(!elScheme && !jiScheme && !lbScheme) {sprintf(DelFile,"log/oDelay%d", NumV);sprintf(CplFile,"log/oCPL%d", NumV);} 

	if (Verbose) {
		if ((MsgLog = fopen (MsgFile, "w")) == NULL) Error(MsgFile);
		if ((EvtLog = fopen (EvtFile, "w")) == NULL) Error(EvtFile);
	}

	if ((DelayLog = fopen (DelFile, "w")) == NULL) Error(DelFile);
	if ((CplLog = fopen (CplFile, "w")) == NULL) Error(CplFile);

	PrintHead ();

	initrandom (Seed);

	for (i=0; i<NumV; i++) PrintLine (i);

	while (Time<SimLength) {
		i = GetNext();
		if (NextLocUpd < Vehicles[i].Next && enabledLocUpd) {
			Time = NextLocUpd; 
			UpdateLocations ();
			NextLocUpd += LocUpd;
		}
		else {
			Time = Vehicles[i].Next;
			Move (i);
		}
		
		ShowProgress(0.003);
	}
	
	PrintSummary ();
	
	fclose(CplLog);
	fclose(DelayLog);
	fclose(DumpLog);

	if (Verbose) {
		fclose (MsgLog);
		fclose (EvtLog);
	}
}
